#ifndef GE_VECTOR2_HPP
#define GE_VECTOR2_HPP

#include <cmath>

#ifdef WIN32
#undef near
#endif

namespace ge {
  class vector2 {
  public:
    vector2(float const x = 0, float const y = 0);
    float const length() const;
    float const length_squared() const;
    float const distance_to(vector2 const & other) const;
    bool const near(vector2 const & other, float const threshold = 1.0f) const;

    vector2 const operator+(vector2 const & rhs) const;
    vector2 const operator-(vector2 const & rhs) const;
    vector2 const operator*(vector2 const & rhs) const;
    vector2 const operator/(vector2 const & rhs) const;  
    vector2 const operator+=(vector2 const & rhs);
    vector2 const operator-=(vector2 const & rhs);
    vector2 const operator*=(vector2 const & rhs);
    vector2 const operator/=(vector2 const & rhs);
    vector2 const operator=(vector2 const & rhs);
    
    static vector2 const zero, unit_x, unit_y;
    float x, y;
    
  private:
    /// This operator is disabled (made private) because it doesn't make much sense for vectors
    bool const operator>(vector2 const & rhs) const;
    /// This operator is disabled (made private) because it doesn't make much sense for vectors
    bool const operator<(vector2 const & rhs) const;
    /// This operator is disabled (made private) because it doesn't make much sense for vectors
    bool const operator>=(vector2 const & rhs) const;
    /// This operator is disabled (made private) because it doesn't make much sense for vectors
    bool const operator<=(vector2 const & rhs) const;
    /// Use vector2::near instead of this because floating point rounding errors could result in a wrong comparison
    bool const operator==(vector2 const & rhs) const;
  };
}

namespace ge {
  inline vector2::vector2(float const x_, float const y_)
    : x(x_), y(y_) { }
  float inline const vector2::length() const {
    return std::sqrt(x * x + y * y);
  }
  float inline const vector2::length_squared() const {
    return x * x + y * y;
  }
  float inline const vector2::distance_to(vector2 const & other) const {
    return std::sqrt((other.x - x) * (other.x - x) + (other.y - y) * (other.y - y));
  }
  bool inline const vector2::near(vector2 const & other, float const threshold) const {
    return (other.x * other.x + other.y * other.y) < threshold * threshold;
  }
  
  vector2 inline const vector2::operator+(vector2 const & rhs) const {
    return vector2(x + rhs.x, y + rhs.y);
  }
  vector2 inline const vector2::operator-(vector2 const & rhs) const {
    return vector2(x - rhs.x, y - rhs.y);
  }
  vector2 inline const vector2::operator*(vector2 const & rhs) const {
    return vector2(x * rhs.x, y * rhs.y);
  }
  vector2 inline const vector2::operator/(vector2 const & rhs) const {
    return vector2(x / rhs.x, y / rhs.y);
  }
  vector2 inline const vector2::operator+=(vector2 const & rhs) {
    x += rhs.x;
    y += rhs.y;
    return *this;
  }
  vector2 inline const vector2::operator-=(vector2 const & rhs) {
    x -= rhs.x;
    y -= rhs.y;
    return *this;
  }
  vector2 inline const vector2::operator*=(vector2 const & rhs)  {
    x *= rhs.x;
    y *= rhs.y;
    return *this;
  }
  vector2 inline const vector2::operator/=(vector2 const & rhs)  {
    x /= rhs.x;
    y /= rhs.y;
    return *this;
  }
  vector2 inline const vector2::operator=(vector2 const & rhs) {
    x = rhs.x;
    y = rhs.y;
    return *this;
  }
  
  bool inline const vector2::operator>(vector2 const & rhs) const {
    return length_squared() > rhs.length_squared();
  }
  bool inline const vector2::operator<(vector2 const & rhs) const {
    return length_squared() < rhs.length_squared();
  }
  bool inline const vector2::operator>=(vector2 const & rhs) const {
    return length_squared() >= rhs.length_squared();
  }
  bool inline const vector2::operator<=(vector2 const & rhs) const {
    return length_squared() <= rhs.length_squared();
  }
  bool inline const vector2::operator==(vector2 const & rhs) const {
    return length_squared() == rhs.length_squared();
  }
  
}

#endif
